洛谷 P2470 [SCOI2007]压缩 题解

Description

一个由小写字母组成的字符串,压缩其重复信息。压缩后的字符串除了小写字母外还可以(但不必)包含大写字母 $R$ 与 $M$,其中 $M$ 标记重复串的开始,$R$ 重复从上一个 $M$(如果当前位置左边没有 $M$,则从串的开始算起)开始的解压结果(称为缓冲串)。

串 $bcdcdcdcd$ 的解压过程:

数据范围:$1<=n<=50$

Solution

设 $f[i][j]$ 为当前处理到第 $i$ 个字符,上个 $M$ 放在了第 $j$ 个字符之后的最小长度。更新的时候使用刷表法。

有三种情况:

$1.$ 什么也不做:$f[i+1][j]=min(f[i+1][j],f[i][j]+1)$

$2.$ 在后面放一个 $M$:$f[i][i]=min(f[i][i],f[i][j]+1)$

$3.$ 在后面放一个 $R$: $f[2i+j][j]=min(f[2i+j][j],f[i][j]+1)$

注意,这里需要判断一下区间 $(j,i]$ 和区间 $(i,i*2-j]$ 是不是完全相同。完全相同才能执行 $3.$这一步。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

char s[100];
int f[60][60], n, ans = 0x3f3f3f3f;

bool get(int mid, int len)
{
if(mid + len > n) return false;
for(int k = 1; k <= len; k++)
if(s[mid - len + k] != s[mid + k])
return false;
return true;
}

int main()
{
scanf("%s", s + 1);
memset(f, 0x3f, sizeof(f));
n = strlen(s + 1);
f[0][0] = 0;
for(int i = 0; i <= n; i++)
{
for(int j = 0; j <= i; j++)
{
f[i][i] = min(f[i][i],f[i][j] + 1);
f[i + 1][j] = min(f[i + 1][j],f[i][j] + 1);
if(get(i, i - j))
f[i * 2 - j][j] = min(f[i * 2 - j][j], f[i][j] + 1);
}
}
for(int i = 0; i <= n; i++) ans = min(ans, f[n][i]);
printf("%d", ans);
return 0;
}